/**********************************************************************************
 * Module Name: tb_cordic_process
 * Description: 
 * Testbench for the cordic_process module.
 * - It populates two BRAM models with sine and cosine values for a unit circle.
 * - It triggers the DUT and waits for completion.
 * - It verifies if the CORDIC magnitude output is close to the expected value 
 * (scaled representation of 1.0).
 **********************************************************************************/
`timescale 1ns / 1ps

module tb_cordic_process;

// Parameters
localparam MUX_NUM = 1536;
localparam CLK_PERIOD = 10; // 10ns = 100MHz clock

// Expected result for sqrt(cos^2 + sin^2) with a scaling factor of 32767
localparam EXPECTED_MAGNITUDE = 32767;
localparam TOLERANCE = 5; // Allow a small error margin for fixed-point CORDIC

// ** FIX 1: Moved PI declaration to the module level **
localparam PI = 3.1415926535; 

// DUT Interface signals
reg  clk;
reg  rst_n;

// Inputs to DUT
reg  [15:0] daout_bram_r_sin_data;
reg  [15:0] daout_bram_r_cos_data;
reg  start_flag;

// Outputs from DUT
wire [$clog2(MUX_NUM)-1:0] daout_bram_r_sin_addr;
wire                        daout_bram_r_sin_en;
wire [$clog2(MUX_NUM)-1:0] daout_bram_r_cos_addr;
wire                        daout_bram_r_cos_en;
wire [15:0]                 result_bram_w_data;
wire [$clog2(MUX_NUM)-1:0] result_bram_w_addr;
wire                        result_bram_w_en;
wire                        result_bram_w_we;
wire                        busy;

// BRAM Models
reg [15:0] sin_bram [0:MUX_NUM-1];
reg [15:0] cos_bram [0:MUX_NUM-1];
reg [15:0] result_bram [0:MUX_NUM-1];

// Instantiate the Device Under Test (DUT)
codic_process #(
    .mux_num(MUX_NUM)
) u_dut (
    .clk(clk),
    .rst_n(rst_n),

    .daout_bram_r_sin_data(daout_bram_r_sin_data),
    .daout_bram_r_sin_addr(daout_bram_r_sin_addr),
    .daout_bram_r_sin_en(daout_bram_r_sin_en),

    .daout_bram_r_cos_data(daout_bram_r_cos_data),
    .daout_bram_r_cos_addr(daout_bram_r_cos_addr),
    .daout_bram_r_cos_en(daout_bram_r_cos_en),

    .result_bram_w_data(result_bram_w_data),
    .result_bram_w_addr(result_bram_w_addr),
    .result_bram_w_en(result_bram_w_en),
    .result_bram_w_we(result_bram_w_we),

    .start_flag(start_flag),
    .busy(busy)
);

// 1. Clock Generation
initial begin
    clk = 0;
    forever #(CLK_PERIOD / 2) clk = ~clk;
end

// 2. Stimulus and Test Sequence
initial begin
    $display("INFO: Simulation Started.");
    // Initial values
    rst_n = 1'b0;
    start_flag = 1'b0;

    // Apply reset
    # (CLK_PERIOD * 5);
    rst_n = 1'b1;
    $display("INFO: Reset released at time %0t.", $time);
    
    # (CLK_PERIOD * 5);

    // Send a single-cycle start pulse
    $display("INFO: Asserting start_flag to begin process.");
    start_flag = 1'b1;
    #CLK_PERIOD;
    start_flag = 1'b0;

    // Wait for the process to start (busy goes high)
    wait (busy == 1'b1);
    $display("INFO: Process started (busy is high). Waiting for completion...");

    // Wait for the process to finish (busy goes low)
    wait (busy == 1'b0);
    $display("INFO: Process finished at time %0t (busy is low).", $time);

    # (CLK_PERIOD * 10); // Wait a bit more before checking

    // 3. Verification
    verify_results();

    $display("INFO: Simulation Finished.");
    $finish;
end

// 4. BRAM Data Initialization
integer i;
initial begin 
    $display("INFO: Initializing SIN and COS BRAMs with sequential test data...");
    for (i = 0; i < MUX_NUM; i = i + 1) begin
        // 顺序整数
        sin_bram[i] = i[15:0];           
        cos_bram[i] = i[15:0]; 
    end
    $display("INFO: BRAMs initialized with sequential integers.");
end

// 5. BRAM Behavioral Models
// Model for SIN and COS BRAM read with 1-cycle latency
always @(posedge clk) begin
    if (daout_bram_r_sin_en) begin
        daout_bram_r_sin_data <= sin_bram[daout_bram_r_sin_addr];
    end
    if (daout_bram_r_cos_en) begin
        daout_bram_r_cos_data <= cos_bram[daout_bram_r_cos_addr];
    end
end

// Model for Result BRAM write
always @(posedge clk) begin
    if (result_bram_w_en && result_bram_w_we) begin
        result_bram[result_bram_w_addr] <= result_bram_w_data;
    end
end

// 6. Verification Task
integer j;
integer error_count;
integer diff;
integer expected_val;
task verify_results;  
begin
    error_count = 0;
    $display("--------------------------------------------------");
    $display("INFO: Starting verification of result_bram...");
    
    for (j = 0; j < MUX_NUM; j = j + 1) begin
        // 动态计算期望值 sqrt(sin^2 + cos^2)
        expected_val = $rtoi($sqrt(
                        ( $itor($signed(sin_bram[j])) * $itor($signed(sin_bram[j])) ) +
                        ( $itor($signed(cos_bram[j])) * $itor($signed(cos_bram[j])) )
                      ));

        diff = result_bram[j] > expected_val ? 
               (result_bram[j] - expected_val) : 
               (expected_val - result_bram[j]);

        if (diff > TOLERANCE) begin
            $display("ERROR: Mismatch at address %d!", j);
            $display("       Input (cos, sin): (%d, %d)", 
                     $signed(cos_bram[j]), $signed(sin_bram[j]));
            $display("       Result: %d, Expected: ~%d", 
                     $signed(result_bram[j]), expected_val);
            error_count = error_count + 1;
        end
    end

    $display("INFO: Verification complete.");
    if (error_count == 0) begin
        $display("SUCCESS: Test Passed!");
    end else begin
        $display("FAILURE: Test Failed with %d errors.", error_count);
    end
    $display("--------------------------------------------------");
end
endtask

endmodule